#include "Memory.h"
#include "Tracer.h"
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>        /* For mode constants */
#include <fcntl.h>           /* For O_* constants */

namespace libemb{

MemBlock::MemBlock()
{
    m_name = "";
    m_isUsed = 0;
    m_address = NULL;
}
MemBlock::~MemBlock()
{
}

MemoryPool::MemoryPool():
m_blockNum(0),
m_memory(NULL)
{
}

MemoryPool::~MemoryPool()
{
    for (int i=0; i < m_blockNum; i++)
    {
        DEL_OBJ(m_memBlocks[i]);
    }
    free(m_memory);
}

bool MemoryPool::initPool(int blockNum, int blockSize)
{
    if (blockNum<=0 || blockSize<=0) 
    {
        return false;
    }
    m_blockNum = blockNum;
    m_blockSize = blockSize;
    m_poolSize = m_blockNum * m_blockSize; 
    m_memory = calloc(m_blockNum,m_blockSize);
    if (m_memory==NULL) 
    {
        return false;
    }

    for (int i=0; i < m_blockNum; i++) 
    {
        MemBlock *memBlock = NEW_OBJ MemBlock();
        memBlock->m_isUsed = false;
        memBlock->m_address = m_memory+(i*m_blockSize);
        m_memBlocks.push_back(memBlock);
    }
    return true;
}

void* MemoryPool::getMemory(const std::string& memoryName, int memorySize)
{
    if (memorySize <= 0 || m_blockSize <= 0 || m_poolSize<=0 || m_blockNum<=0 || memoryName.empty()) 
    {
        return NULL;
    }
    int blocks = (memorySize + m_blockSize-1)/m_blockSize;
    int num=0;
    for (int i=0; i < m_blockNum; i++) 
    {
        if (!m_memBlocks[i]->m_isUsed) 
        {
            num++;
            if (num==blocks) 
            {
                for (int j=0;j<blocks;j++) 
                {
                    m_memBlocks[i-j]->m_isUsed=true;
                    m_memBlocks[i-j]->m_name = memoryName;
                }
                return m_memBlocks[i-blocks+1]->m_address;
            }
        }
        else
        {
            num=0;
        }
    }
    return NULL;
}

bool MemoryPool::putMemory(const std::string& memoryName)
{
    if (m_blockSize <= 0 || m_poolSize<=0 || m_blockNum<=0) 
    {
        return false;
    }
    for (int i=0; i<m_blockNum; i++) 
    {
        if (m_memBlocks[i]->m_name==memoryName && 
            m_memBlocks[i]->m_isUsed) 
        {
            m_memBlocks[i]->m_name="";
            m_memBlocks[i]->m_isUsed=false;
        }
    }
    return true;
}
void MemoryPool::showMemory()
{
    for (int i=0; i<m_blockNum; i++)
    {
        TRACE_DBG("name:%s,use:%d,block:%d,addr:%08x\n",
                  m_memBlocks[i]->m_name.c_str(),m_memBlocks[i]->m_isUsed,
                  i,m_memBlocks[i]->m_address);
    }
}
MemShared::MemShared(int type):
m_shmSize(0),
m_shmAddr(NULL),
m_shmType(type)
{
}
MemShared::~MemShared()
{
}
bool MemShared::open(const char *name, int ioMode)
{
    if (NULL==name ||
		(ioMode < 0) ||
		(ioMode > IO_MODE_APPEND_ONLY))
	{
		TRACE_ERR_CLASS("Parameter error.\n");
		return false;
	}
    
    if (m_fd >= 0)
	{
		TRACE_ERR_CLASS("Device is already opened!\n");
		return false;
	}
    if(m_shmType==MEMSHARED_TYPE_SHM)
	{
    	switch(ioMode)
    	{
    		case IO_MODE_RD_ONLY:
    			m_fd = ::shm_open(name, O_RDONLY,0666);
    			break;
    		case IO_MODE_RDWR_ONLY:
    			m_fd = ::shm_open(name, O_RDWR|O_CREAT,0666);
    			break;
    		default:
    			TRACE_ERR_CLASS("Unsupport SHM IO Mode: %d\n",ioMode);
    			return false;
    	}
    }
    else
    {
        switch(ioMode)
    	{
    		case IO_MODE_RD_ONLY:
    			m_fd = ::open(name, O_RDONLY,0666);
    			break;
            case IO_MODE_WR_ONLY:
    			m_fd = ::open(name, O_WRONLY|O_CREAT,0666);
    			break;
    		case IO_MODE_RDWR_ONLY:
    			m_fd = ::open(name, O_RDWR|O_CREAT,0666);
    			break;
    		default:
    			TRACE_ERR_CLASS("Unsupport FILE IO Mode : %d\n",ioMode);
    			return false;
    	}
    }
    
	if (m_fd<0)
	{
		TRACE_ERR_CLASS("Open %s error: %s\n",name,ERROR_STRING);
		return false;
	}
    
    if (-1==ftruncate(m_fd,m_shmSize))
    {
        TRACE_ERR_CLASS("shm[%s] set size(%d) error:%s!\n",m_devName.c_str(),m_shmSize,ERROR_STRING);
        return false;    
    }
    struct stat fdStat;
    if (-1==fstat(m_fd,&fdStat))
    {
        TRACE_ERR_CLASS("shm[%s] fstat error:%s!\n",m_devName.c_str(),ERROR_STRING);
        return false;
    }
    m_shmSize=fdStat.st_size;
    m_devName = name;
    m_openMode = ioMode;
	return true;  
}

bool MemShared::close()
{
    if (m_shmType==MEMSHARED_TYPE_SHM)
    {
        if (shm_unlink(m_devName.c_str())!=0)
        {
            TRACE_ERR_CLASS("shm[%s] unlink error:%s!\n",m_devName.c_str(),ERROR_STRING);
            return false;
        }
    }
    else
    {
        if ((m_fd>0)&&(::close(m_fd)!=0))
        {
            TRACE_ERR_CLASS("shm file[%s] close error:%s!\n",m_devName.c_str(),ERROR_STRING);
            return false;
        }
    }
    return true;
}

int MemShared::setAttribute(int attr, int value)
{
    if (m_fd >=0 )
	{
		TRACE_ERR_CLASS("Device has been opened,can't set attribute!\n");
		return STATUS_ERROR;
	}
    switch(attr)
    {
        case MEMSHARED_ATTR_SIZE:
            m_shmSize=value;
            break;
        default:
            TRACE_ERR_CLASS("Unsupport attr:%d.\n",attr);
            return STATUS_ERROR;
    }
}
int MemShared::getAttribute(int attr)
{
    struct stat fdStat;
    if (m_fd<0)
    {
        TRACE_ERR_CLASS("Device has not been opened,can't get attribute!\n");
		return STATUS_ERROR;
    }
    switch(attr)
    {
        case MEMSHARED_ATTR_SIZE:
            if (-1==fstat(m_fd,&fdStat))
            {
                TRACE_ERR_CLASS("shm[%s] fstat error:%s!\n",m_devName.c_str(),ERROR_STRING);
                return STATUS_ERROR;
            }
            m_shmSize=fdStat.st_size;
            return m_shmSize;
        default:
            TRACE_ERR_CLASS("Unsupport attr:%d.\n",attr);
            return STATUS_ERROR;
    }
}
/**
 *  \brief  创建并绑定共享内存
 *  \param  none
 *  \return 成功返回内存地址,失败返回NULL
 *  \note   none
 */
void* MemShared::attach()
{    
    void* addr = mmap(NULL,m_shmSize,PROT_READ|PROT_WRITE,MAP_SHARED,m_fd,0);
    if (MAP_FAILED==addr)
    {
        TRACE_ERR_CLASS("shm[%s] map error:%s!\n",m_devName.c_str(),ERROR_STRING);
        return NULL;    
    }
    return m_shmAddr=addr;
}

/**
 *  \brief  销毁共享内存区域
 *  \param  none
 *  \return 成功返回STATUS_OK,失败返回STATUS_ERROR
 *  \note   none
 */
int MemShared::detach()
{
    if (munmap(m_shmAddr,m_shmSize)!=0)
    {
        return STATUS_ERROR;   
    }
    return STATUS_OK;
}

int MemShared::readData(char *buf, int count, int usTimeout)
{
    return STATUS_ERROR;
}
int MemShared::writeData(const char *buf, int count, int usTimeout)
{
    return STATUS_ERROR;
}
}
